#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <syslog.h>
#include <unistd.h>
#include <pwd.h>
#include <mysql/mysql.h>

#include "common.h"
#include "daemonize.h"
#include "httpd.h"
#include "xml.h"

char page_root[256];
char log_file[128];
char default_page[128];
struct xml *mode = NULL;
MYSQL *mysql_connect = NULL;

static char listen_port[6];
static char config_file[128];
static char mode_file[64];

/* database */
static char database_host[64];
static char database_username[64];
static char database_password[64];
static char database_name[64];
static char database_port[8];

extern char *optarg;

static void
usage(const char *name)
{
    fprintf(stderr,
    "Usage: %s -h diretory [-p port] [-l file]\n"
    "   -h directory  Sercify the document root\n"
    "   -l file Log file\n"
    "   -p port Listen port\n"
    "\n", name
    );
}

/* 默认配置 */
static void
initialize_config(void)
{
    /* 默认配置文件 */
    memset(config_file, 0, sizeof(config_file));
    snprintf(config_file, sizeof(config_file), "%s", "config.xml");

    /* mode文件 */
    memset(mode_file, 0, sizeof(mode_file));
    snprintf(mode_file, sizeof(mode_file), "%s", "mode.xml");

    /* web默认根目录 */
    memset(page_root, 0, sizeof(page_root));
    snprintf(page_root, sizeof(page_root), "%s", ".");

    /* 日志文件默认名 */
    memset(log_file, 0, sizeof(log_file));
    snprintf(log_file, sizeof(log_file), "%s/%s", page_root, "httpd.log");

    /* 默认首页面 */
    memset(default_page, 0, sizeof(default_page));
    snprintf(default_page, sizeof(default_page), "/%s", "index.html");
    
    /* 默认监听端口 */
	memset(listen_port, 0, sizeof(listen_port));
    snprintf(listen_port, sizeof(listen_port), "%d", LISTENPORT);

	/* 数据库host */
	memset(database_host, 0, sizeof(database_host));
	snprintf(database_host, sizeof(database_host), "%s", "localhost");

	/* 数据库登录用户名 */
	memset(database_username, 0, sizeof(database_username));
	snprintf(database_username, sizeof(database_username), "%s", "root");

	/* 数据库登录密码 */
	memset(database_password, 0, sizeof(database_password));
	snprintf(database_password, sizeof(database_password), "%s", "123456");

	/* 数据库名 */
	memset(database_name, 0, sizeof(database_name));
	snprintf(database_name, sizeof(database_host), "%s", "mysql");

	/* 数据库名 */
	memset(database_port, 0, sizeof(database_port));
	snprintf(database_port, sizeof(database_port), "%s", "3306");

}

/* web初始化*/
static int 
initialize_web_config(struct xml *web)
{
	struct xml *tmp = NULL;
    /* 获取web根目录 */
	if ((tmp = get_first_xml_object(web, "web-root")) != NULL && tmp->type == XML_ELEMENT)
	{
        memset(page_root, 0, sizeof(page_root));
		snprintf(page_root, sizeof(page_root), "%s", tmp->value);
		if (chroot(page_root) < 0)  /* 设置当前web根目录 */
		{
			printf("chdir error: %s\n", strerror(errno));
            return (-1);
		}
        printf("chroot to '%s'\n", page_root);
	}

    /* 获取默认首页 */
	if ((tmp = get_first_xml_object(web, "default-page")) != NULL && tmp->type == XML_ELEMENT)
	{
		printf("default page: %s\n", tmp->value);
		snprintf(default_page, sizeof(default_page), "%s", tmp->value);
	}

    /* 获取web监听端口 */
	if ((tmp = get_first_xml_object(web, "listen-port")) != NULL && tmp->type == XML_ELEMENT)
	{
		printf("listen port: %s\n", tmp->value);
		snprintf(listen_port, sizeof(listen_port), "%s", tmp->value);
	}
    return (0);
}

/* 初始化数据库 */
static MYSQL *
initialize_database(struct xml *x)
{
	struct xml *tmp = NULL;
    /* 获取数据库host */
	if ((tmp = get_first_xml_object(x, "database-host")) != NULL && tmp->type == XML_ELEMENT)
	{
		printf("database host: %s\n", tmp->value);
		snprintf(database_host, sizeof(database_host), "%s", tmp->value);
	}

	/* 数据库登录名 */
	if ((tmp = get_first_xml_object(x, "database-username")) != NULL && tmp->type == XML_ELEMENT)
	{
		printf("database username: %s\n", tmp->value);
		snprintf(database_username, sizeof(database_username), "%s", tmp->value);
	}

	/* 数据库登录密码 */
	if ((tmp = get_first_xml_object(x, "database-password")) != NULL && tmp->type == XML_ELEMENT)
	{
		printf("database password: %s\n", tmp->value);
		snprintf(database_password, sizeof(database_password), "%s", tmp->value);
	}

	/* 数据库名 */
	if ((tmp = get_first_xml_object(x, "database-name")) != NULL && tmp->type == XML_ELEMENT)
	{
		printf("database name: %s\n", tmp->value);
		snprintf(database_name, sizeof(database_name), "%s", tmp->value);
	}

	/* 数据库监听端口 */
	if ((tmp = get_first_xml_object(x, "database-port")) != NULL && tmp->type == XML_ELEMENT)
	{
		printf("database port: %s\n", tmp->value);
		snprintf(database_port, sizeof(database_port), "%s", tmp->value);
	}

	/* 初始化数据库句柄 */
    if ((mysql_connect = mysql_init(NULL)) == NULL)
    {
		printf("mysql_init error: %s\n", mysql_error(mysql_connect));
        return (NULL);
    }

	/* 连接数据库 */
    if (!mysql_real_connect(mysql_connect, database_host, database_username, database_password, database_name, atoi(database_port), NULL, 0))
	{
		printf("mysql_real_connect error: %s\n", mysql_error(mysql_connect));
		mysql_close(mysql_connect);
		return (NULL);
	}

    return (mysql_connect);
}



static void
load_config_xml(void)
{
    /* 打开并读取配置文件 */
	FILE *fp = NULL;
	char xml_data[4096];
	if ((fp = fopen(config_file, "r")) == NULL)
	{
		printf("fopen config.xml error\n");
		return ;
	}
	fread(xml_data, 1, sizeof(xml_data), fp);

    /* 将文件内容解成xml格式 */
	struct xml *config = NULL;
	if ((config = load_xml(xml_data)) == NULL)
	{
		printf("load_xml error\n");
		return ;
	}
    initialize_web_config(config);
	initialize_database(config);

	destroy_xml(config);
}

/*
 * 加载mode.xml文件
 * 注意:返回0时表示成功,此时必须手动释放申请的内存
 */
static int
load_mode_xml(void)
{
    /* 打开并读取mode文件 */
	FILE *fp = NULL;
	char xml_data[4096];
	if ((fp = fopen(mode_file, "r")) == NULL)
	{
		printf("fopen mode.xml error\n");
		return (-1);
	}
	fread(xml_data, 1, sizeof(xml_data), fp);

    /* 将文件内容解成xml格式 */
	if ((mode = load_xml(xml_data)) != NULL)
	{
		return (0);
	}
    printf("load_xml error\n");
    return (-1);
}

int
main(int argc, char *argv[])
{
//	daemonize(argv[0]);
    
    /* 初始化配置 */
    initialize_config();

	/* 运行参数 */
	int opt;
	while (-1 != (opt = getopt(argc, argv, "h:f:")))
	{
		switch (opt)
		{
		case 'h':
           usage(argv[0]);
           return (0);
        case 'f':
           memset(config_file, 0, sizeof(config_file));
           snprintf(config_file, sizeof(config_file), "%s", optarg);
           break;
		}
	}

    /* 加载配置文件 */
	load_config_xml();
    load_mode_xml();

    /* 服务配置 */
	//1.server addr
	struct sockaddr_in listen_addr;
	listen_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	listen_addr.sin_port = htons(atoi(listen_port));
	listen_addr.sin_family = AF_INET;

	//2.create a socket
	int listen_fd;
	if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		printf("socket error: %s\n", strerror(errno));
		exit(1);
	}
	int resuse_addr = 1;
	if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &resuse_addr, sizeof(resuse_addr)) < 0)
	{
		printf("setsockopt error: %s\n", strerror(errno));
		exit(1);
	}

	//3.bind
	if (bind(listen_fd, (struct sockaddr *)&listen_addr, sizeof(listen_addr)) < 0)
	{
		printf("bind error: %s\n", strerror(errno));
		exit(1);
	}

	//4.listen
	if (listen(listen_fd, BACKLOG) < 0)
	{
		printf("listen error: %s\n", strerror(errno));
		exit(1);
	}

	for (;;)
	{
		printf("wait for client....\n");

		//5.wait for client access
		int accept_fd = -1;
		struct sockaddr addr;
		socklen_t addrlen = 0;
		if ((accept_fd = accept(listen_fd, (struct sockaddr *)&addr, &addrlen)) < 0)
		{
			if (errno == EINTR)
			{
				continue;
			}
			close(listen_fd);
			return (-1);
		}

		//get client info
		struct sockaddr_in cliaddr;
		socklen_t cliaddrlen = sizeof(cliaddr);
		if (getpeername(accept_fd, (struct sockaddr *)&cliaddr, &cliaddrlen) < 0)
		{
			printf("getpeername error: %s\n", strerror(errno));
		}
		else
		{
			printf("connect by %s:%d\n", inet_ntoa(cliaddr.sin_addr), cliaddr.sin_port);
		}

		/*
		 * if fork() success, the parent and child process will wach have 
		 * an accept_fd and listen_fd, so we should close the listen_fd socket 
		 * of the child process, and the accept_fd socket of the parent process
		 */
		//6.create a process to receive data.
		pid_t pid;
		if ((pid = fork()) < 0)
		{
			printf("fork error: %s\n", strerror(errno));
			close(listen_fd);
			close(accept_fd);

			return (-1);
		}
		else if (pid == 0) // first child
		{
			/*
			 * only keep the listen_fd of the parent process
			 */
			close(listen_fd);
			handle_request(accept_fd);
            destroy_xml(mode);
		}

		/*
		 * parent of first child.
		 * only keep the accept_fd of the first child process
		 */
		close(accept_fd);
		if (waitpid(pid, NULL, 0) != pid)
		{
			printf("waitpid error\n");
		}
	}
	return 0;
}
